package org.dding.web.console;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

import org.dding.core.annotation.Service;
import org.dding.core.log.ILogger;
import org.dding.core.log.LoggerFactory;
import org.dding.core.util.StringUtils;
import org.dding.mvc.ActionSupport;
import org.dding.mvc.InvocationContext;
import org.dding.mvc.annotation.ActionMethod;
import org.dding.mvc.annotation.ActionMethod.ForwardMethod;
import org.dding.mvc.util.Pager;

@Service(name = "loggerAction")
public class LoggerAction extends ActionSupport<Object> {

	private ILogger logger = LoggerFactory.getLogger(getClass());
	private Properties props = new Properties();
	private volatile AtomicBoolean init = new AtomicBoolean(false);
	LogManager manager = LogManager.getLogManager();
	List<LoggerInfo> result;
	private static String[] properties = { "loggerName", "level" };
	private static Map<String, Comparator<LoggerInfo>> mapping = new HashMap<String, Comparator<LoggerInfo>>();

	static {
		for (String property : properties) {
			String method = "get" + property.substring(0, 1).toUpperCase()
					+ property.substring(1);
			mapping.put(property, new InternalComparator<LoggerInfo>(method));
		}
	}

	public LoggerAction() {
		try {
			result = readConfiguration();
			if (result != null)
				init.compareAndSet(false, true);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@ActionMethod(
			name = "add",
			successView = "main/loglist.jsp",
			httpMethod = ActionMethod.HttpMethod.POST)
	public void add(InvocationContext invocationContext) {
		String name = invocationContext.getParameterValue("name");
		String level = invocationContext.getParameterValue("level");
		Level format = Level.parse(level.trim());
		if (format == null) {
			throw new IllegalArgumentException("logger level is invalid.");
		}
		props.setProperty(name + ".level", level);
		try {
			saveAndReload();
		} catch (IOException e) {
			logger.warn(e);
		}
		invocationContext.getPageContext().setAttribute("pager",
				handlePager(result, null));
	}

	@ActionMethod(
			name = "search",
			successView = "main/loglist.jsp",
			invocationClass = Pager.class)
	public void search(InvocationContext invocationContext) {
		Pager pagerInvocaion = (Pager) invocationContext.getInvocation();

		String keyword = pagerInvocaion.getKeyword();
		String searchBy = pagerInvocaion.getSearchBy();

		if (StringUtils.isNotEmpty(keyword) && StringUtils.isNotEmpty(searchBy)) {
			query(invocationContext);
		} else {
			if (pagerInvocaion != null) {
				Pager pager = handlePager(result, pagerInvocaion);
				String orderBy = pager.getOrderBy();
				if (StringUtils.isNotEmpty(orderBy)) {
					Comparator<LoggerInfo> comparator = mapping.get(orderBy);
					if (comparator != null)
						Collections.sort(result, comparator);
				}
				invocationContext.getPageContext().setAttribute("pager", pager);
			}
		}
	}

	private void query(InvocationContext invocationContext) {
		Pager pagerInvocaion = (Pager) invocationContext.getInvocation();
		String keyword = pagerInvocaion.getKeyword();
		String searchBy = pagerInvocaion.getSearchBy();
		List<LoggerInfo> list = new ArrayList<LoggerInfo>();
		if ("loggerName".equalsIgnoreCase(searchBy)) {
			for (LoggerInfo info : result) {
				if (StringUtils.containsIgnoreCase(info.getLoggerName(),
						keyword)) {
					list.add(info);
				}
			}
		} else if ("level".equalsIgnoreCase(searchBy)) {
			for (LoggerInfo info : result) {
				if (StringUtils.containsIgnoreCase(info.getLevel(), keyword)) {
					list.add(info);
				}
			}
		} else {

			throw new IllegalArgumentException(
					"only support search by resource name.");
		}
		invocationContext.getPageContext().setAttribute("pager",
				handlePager(list, null));
	}

	@ActionMethod(
			name = "delete",
			successView = "main/loglist.jsp",
			httpMethod = ActionMethod.HttpMethod.POST,
			forwardMethod = ForwardMethod.FLUSH)
	public void delete(InvocationContext invocationContext) {

		String[] ids = invocationContext.getParameterValues("ids");
		for (String id : ids) {
			props.remove(id + ".level");
		}
		Map<String, String> result = new HashMap<String, String>();
		try {
			saveAndReload();
			result.put(STATUS, SUCCESS);
			result.put(MESSAGE, "删除成功!");
		} catch (IOException e) {
			result.put(STATUS, FAILURE);
			result.put(MESSAGE, "删除失败" + e.getMessage());
		}
		ajaxJsonMessage(result, invocationContext);
	}

	@ActionMethod(
			name = "modify",
			successView = "main/loglist.jsp",
			httpMethod = ActionMethod.HttpMethod.GET)
	public void modify(InvocationContext invocationContext) {
		String name = invocationContext.getParameterValue("name");
		String level = invocationContext.getParameterValue("level");

		if (name == null && level == null)
			return;
		name = name + ".level";

		Level formatLevel = Level.parse(level.trim());
		boolean modify = false;
		if (formatLevel != null) {
			String value = (String) props.get(name);
			if (value != null) {
				if (!value.equalsIgnoreCase(formatLevel.getName())) {
					props.setProperty(name, formatLevel.getName());
					modify = true;
				}
			} else {
				props.setProperty(name, formatLevel.getName());
				modify = true;
			}
		}
		if (modify) {
			try {
				saveAndReload();
			} catch (IOException e) {
				logger.warn(e);
			}
		}
		invocationContext.getPageContext().setAttribute("pager",
				handlePager(result, null));

	}

	private void saveAndReload() throws IOException {
		String fname = System.getProperty("java.util.logging.config.file");
		OutputStream output = new FileOutputStream(new File(fname));
		props.store(output, null);
		manager.readConfiguration();
		result = readConfiguration();
	}

	@ActionMethod(
			name = "list",
			successView = "main/loglist.jsp",
			httpMethod = ActionMethod.HttpMethod.GET)
	public void list(InvocationContext invocationContext) {
		if (init.get())
			invocationContext.getPageContext().setAttribute("pager",
					handlePager(result, null));

	}

	private List<LoggerInfo> readConfiguration() throws IOException {

		String fname = System.getProperty("java.util.logging.config.file");
		InputStream in = null;
		try {
			in = new FileInputStream(fname);
			BufferedInputStream bin = new BufferedInputStream(in);
			props.load(bin);
		} finally {
			if (in != null) {
				in.close();
			}
		}
		List<LoggerInfo> list = new ArrayList<LoggerInfo>();
		Enumeration<?> enum_ = props.propertyNames();
		while (enum_.hasMoreElements()) {
			String key = (String) enum_.nextElement();
			if (!key.endsWith(".level")) {
				// Not a level definition.
				continue;
			}
			int ix = key.length() - 6;
			String name = key.substring(0, ix);
			Level level = getLevelProperty(key, null);
			if (level == null) {
				System.err.println("Bad level value for property: " + key);
				continue;
			}
			Logger l = manager.getLogger(name);
			if (l == null) {
				continue;
			}
			LoggerInfo loggerInfo = new LoggerInfo();
			loggerInfo.setLoggerName(name);
			loggerInfo.setLevel(level.getName());
			list.add(loggerInfo);
		}
		return list;
	}

	Level getLevelProperty(String name, Level defaultValue) {
		String val = manager.getProperty(name);
		if (val == null) {
			return defaultValue;
		}
		try {
			return Level.parse(val.trim());
		} catch (Exception ex) {
			return defaultValue;
		}
	}

}
